#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "log.hpp"
#include "err.hpp"

namespace TCP
{
    // 公用 socket
    class SockBase
    {
    public:
        void Socket()
        {
            _sock = socket(AF_INET, SOCK_STREAM, 0);
            if (_sock < 0)
            {
                logMessage(Fatal, "TCP socket error! code:%d,string error:%s", errno, strerror(errno));
                exit(SOCK_ERR);
            }
            int opt = 1; //设置复用ip和端口 防止服务器挂掉TIME_WAIT状态导致服务器无法启动
            if(setsockopt(_sock,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,(void*)&opt,sizeof(opt)) != 0)
            {
                logMessage(Warning, "setsockopt socket error! code:%d,string error:%s", errno, strerror(errno));
            }
        }

        int GetSockFd()
        {
            if (_sock < 0)
            {
                logMessage(Error, "No socket! code:%d,string error:%s", errno, strerror(errno));
            }
            return _sock;
        }
        void Close_Sockfd() { close(_sock); _sock = -1;}

    protected:
        int _sock = -1;
    };

    // 服务器 bind listen accept
    class SockServer : public SockBase
    {
    public:
        SockServer(const uint16_t &port)
            : _port(htons(port))
        {}

        void Bind()
        {
            if (_sock < 0)
            {
                Socket();
            }
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = _port;
            local.sin_addr.s_addr = INADDR_ANY;
            int n = bind(_sock, (struct sockaddr *)(&local), sizeof(local));
            if (n < 0)
            {
                logMessage(Fatal, "TCP bind error! code:%d,string error:%s", errno, strerror(errno));
                exit(BIND_ERR);
            }
            logMessage(Info, "TCP bind success! code:%d,string error:%s", errno, strerror(errno));
        }

        // 设置为监听状态
        void Listen(int n = 32) //默认全链接队列数为32
        {
            int lit = listen(_sock, n);
            if (lit < 0)
            {
                logMessage(Fatal, "TCP set listen error! code:%d,string error:%s", errno, strerror(errno));
                exit(LISTEN_ERR);
            }
            logMessage(Info, "TCP set listen success! code:%d,string error:%s", errno, strerror(errno));
        }

        int Accept(std::string *clientip, uint16_t *clientport) // 监听成功就输出客户端的ip和port并返回文件描述符
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int client_sock = accept(_sock, (struct sockaddr *)(&client), &len);
            if (client_sock < 0)
            {
                logMessage(Warning, "TCP accept error! code:%d,string error:%s", errno, strerror(errno));
            }
            else
            {
                if(clientip) *clientip = inet_ntoa(client.sin_addr);
                if(clientport) *clientport = ntohs(client.sin_port);
            }
            logMessage(Info, "TCP accept success! code:%d,string error:%s", errno, strerror(errno));
            return client_sock;
        }

    protected:
        uint16_t _port;
    };

    // 客户端 connect
    class SockClient : public SockBase
    {
    public:
        SockClient(const std::string &ip, const uint16_t &port)
            : _ip(ip) // server ip
              ,
              _port(port) // server port
        {
        }
        int Connect()
        {
            if (_sock < 0)
            {
                Socket();
            }
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = inet_addr(_ip.c_str());

            return connect(_sock, (struct sockaddr *)(&local), sizeof(local));
        }

    protected:
        std::string _ip;
        uint16_t _port;
    };
}

namespace UDP
{
    // 公用 socket
    class SockBase
    {
    public:
        void Socket()
        {
            _sock = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sock < 0)
            {
                logMessage(Fatal, "UDP socket error! code:%d,string error:%s", errno, strerror(errno));
                exit(SOCK_ERR);
            }
        }

        int GetSockFd()
        {
            if (_sock < 0)
            {
                logMessage(Error, "No socket! code:%d,string error:%s", errno, strerror(errno));
            }
            return _sock;
        }
        void Close_Sockfd() { close(_sock); _sock = -1;}

    protected:
        int _sock = -1;
    };

    // 服务器 bind
    class SockServer : public SockBase
    {
        const int gbacklog = 32; // 监听等待队列的最多输了
    public:
        SockServer(const uint16_t &port)
            : _port(htons(port))
        {
        }

        void Bind()
        {
            if (_sock < 0)
            {
                Socket();
            }
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = _port;
            local.sin_addr.s_addr = INADDR_ANY;

            int n = bind(_sock, (struct sockaddr *)(&local), sizeof(local));
            if (n < 0)
            {
                logMessage(Fatal, "UDP bind error! code:%d,string error:%s", errno, strerror(errno));
                exit(BIND_ERR);
            }
            logMessage(Info, "UDP bind success! code:%d,string error:%s", errno, strerror(errno));
        }

    protected:
        uint16_t _port;
    };

    class SockClient : public SockBase
    {
    };
}